#! /usr/bin/env python
r''' 

``bin2ascii.py``

	:date: 20. Okt. 2011

'''

base64_alp=\
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
'''Normal ``base64`` alphabet'''

base64_alp_save=\
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
'''URL-save ``base64`` alphabet'''


base32_alp=\
"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
'''The ``base32`` alphabet'''

pad_char = "="
'''the padding character
'''



class base64encoder():#
	'''
Verschluesst einen String in `base64`.
	'''
	b64_m = int("00111111",2)
	
	def _process3byte(self, bytestr, alph):#
		'''
		Verschluesselt einen String der Laenge 4 Byte, welcher die base 64 Repraesentation
		des Eingabestrings ist.
		
		:param bytestr: Eingabestring der Laenge 3 Byte
		:param alph: ein String der Laenge 65. Dieser String repraesentiert das Alphabet.
		:return: base 63 Verschluesslung der 3 Eingabe Bytes
		:rtype: Ein String der Laenge 4 Bytes
		
		.. Note::
		
			private Methode
		'''
		inputBytes = [ord(bytestr[0]),\
				 ord(bytestr[1]),
				 ord(bytestr[2])]
		#print bytes
		string = [0,0,0,0]
		string[0] = alph[
				inputBytes[0] >> 2 & base64encoder.b64_m ]
		string[1] = alph[ 
				((inputBytes[0] << 6) >> 2 | inputBytes[1] >> 4) & base64encoder.b64_m] 
		string[2] = alph[ 
				((inputBytes[1] << 4) >> 2 | inputBytes[2] >> 6) & base64encoder.b64_m] 
		string[3] = alph[ 
				inputBytes[2] & base64encoder.b64_m ]
		#print ''.join(string) 
		
		return ''.join(string) 
	
	def str2base64(self,data,urlsave=False):#
		'''
			encode data to base64.
			
				:param data: data to be encoded
				:param urlsave: default: ``False``, if 	``True`` the URL-save 
					alphabet is used, else the normal alphabet
				:return: base64 encoding of the input string
				:rtype: ``str``   
		'''
		base64_result = []
		
		# determine if use URL save or not
		alph = base64_alp
		if urlsave:
			alph = base64_alp_save
		
		# j is a counter 
		j = 0
		# first we process all 3-er groups if possible, using the
		# method ::self._process3byte::
		for i in range(0, len(data), 3):
			if (len(data)-j) < 3:
				break
			block_data = data[i:i+3]
			j = j+3
			
			base64_result.append(
				self._process3byte(block_data, alph ))
		
		
		# then we process the rest, which cannot fix in a 3-byte group
		if len(data) - j == 1:# 8 bit rest
			base64_result.append(
				alph[ (ord(data[j]) >> 2 ) & base64encoder.b64_m ])
			base64_result.append(
				alph[ ( ord(data[j]) << 6 ) >> 2 & base64encoder.b64_m ])
			base64_result.append(pad_char*2)
			
		elif len(data) - j == 2:#16 bit
			base64_result.append(
				alph[ (ord(data[j])    >> 2) & base64encoder.b64_m])
			base64_result.append(
				alph[ ((ord(data[j])   << 6) >> 2 | ord(data[j+1]) >> 4) & base64encoder.b64_m])
			base64_result.append(
				alph[ ((ord(data[j+1]) << 4) >> 2 | 0) & base64encoder.b64_m])			
			base64_result.append(pad_char)
			
		result = "".join(base64_result)
		return result


	def _process4byte(self,block,alph):
		'''
encode a 4 byte block of a base64 String (without padding)

:param block: a 4 byte block of a base64 string
:param alph: the alphabet of base64
:return: 3 byte- string
:rtype: ``str``

.. Note:: 
	private method
		'''
		
		mask1 = int("00001111",2)
		mask2 = int("00000011",2)
		string_result = [0,0,0]
		
		string_result[0] =chr(
			(alph.index(block[0]) << 2) | (alph.index(block[1]) >> 4))
		string_result[1] =chr(
			(alph.index(block[1]) & mask1) << 4 | (alph.index(block[2]) >> 2))
		string_result[2] =chr(
			(alph.index(block[2]) & mask2) << 6 | alph.index(block[3]) )
	
		
		result = "".join(string_result)
		return result
		pass
	
	def _process_last_4byte_block(self,block,alph):#
		'''
convert the last 4-byte block. There are to cases: 

* The block contains one padding character,
* the block contains two padding characters and
* the block does not contain any padding characters. 

:param block: the last block, always 4 byte
:param alph: the base64 alphabet
:return: the raw data, it can be 3, 2 or one bytes
:rtype: ``str``

.. Note::
	private method
		'''
		
		mask1 = int("00001111",2)
		string_result = ["","",""]
		result = None
		
		
		if(block[2] == pad_char):
			string_result[0] =chr(
				(alph.index(block[0]) << 2) | (alph.index(block[1]) >> 4))
			result = ''.join(string_result)
		elif (block[3]==pad_char):	
			string_result[0] =chr(
				(alph.index(block[0]) << 2) | (alph.index(block[1]) >> 4))
			string_result[1] =chr(
				(alph.index(block[1]) & mask1) << 4 | (alph.index(block[2]) >> 2))
			result = ''.join(string_result)
		else:	
			result = self._process4byte(block, alph)
		return result	
		
	def base642str(self,base64_str,urlsave=False):#
		'''
convert the base 64 presentation of data to raw data.

:param base64_str: the base 64 presentation of data (type ``str``)
:param urlsave: if it is ``True``, the url-save alphabet is used, else the normal
	default is ``False``
:return: the raw data
:rtype: ``str``
		'''
		data_result = []
		alph = base64_alp
		if urlsave:
			alph = base64_alp_save
		
		i = 0	
		for i in range(0,len(base64_str)-4, 4):
			buff = base64_str[i:i+4]
			data_result.append(self._process4byte(buff, alph))
		
		data_result.append(
			self._process_last_4byte_block(base64_str[-4:],alph))
		result = ''.join(data_result)
		return result
		


class base32encoder():#
	'''

	'''
	b32_m = int("00011111",2)
	def _process5byte(self,bytesstr):
		'''
process a block of 5 bytes

:param bytesstr: the block of 5 bytes
:return: a 8byte string
:rtype: str
		'''
		string = [0,0,0,0, 0,0,0,0]
		inputBytes = [
			ord(bytesstr[0]), ord(bytesstr[1]), ord(bytesstr[2]), 
			ord(bytesstr[3]), ord(bytesstr[4])
					]
		alph = base32_alp
		string[0] = alph[
				( inputBytes[0] >> 3) & base32encoder.b32_m ]
		string[1] = alph[
				((inputBytes[0] << 2) | inputBytes[1] >> 6) & base32encoder.b32_m ]
		string[2] = alph[
				(inputBytes[1] >> 1) & base32encoder.b32_m ]
		string[3] = alph[
				((inputBytes[1] << 4) | inputBytes[2] >> 4) & base32encoder.b32_m ]
		string[4] = alph[
				((inputBytes[2] << 1) | inputBytes[3] >> 7) & base32encoder.b32_m ]
		string[5] = alph[
				(inputBytes[3] >> 2) & base32encoder.b32_m ]
		string[6] = alph[
				((inputBytes[3] << 3) | inputBytes[4] >> 5) & base32encoder.b32_m ]
		string[7] = alph[
				inputBytes[4] & base32encoder.b32_m]
		
		result = ''.join(string)
		return result
		pass
	
	
	def _process_last_block(self,block):#
		'''
process the last block of input data, the length of block varies fon 1 two 4

:param block: the input block
:return: the string represents the input block in base32
:rtype: str

.. Note::
	private method 
		'''
		
		inputBytes = []
		string = []
		for i in block:
			inputBytes.append(ord(i))
		alph = base32_alp
		# one byte ~ 8 bits    => set string[0] and string[1] 
		string.append( alph [ (inputBytes[0] >> 3) & base32encoder.b32_m])
		string.append( alph [ (inputBytes[0] << 2) & base32encoder.b32_m])
		pad_count = 6
		# two bytes ~ 16 bits  => set string[1] string[2] and string[3]
		if len(inputBytes) > 1:
			string[1]= alph[
						((inputBytes[0] << 2) | inputBytes[1] >> 6) & base32encoder.b32_m ]
			string.append(alph[
						(inputBytes[1] >> 1) & base32encoder.b32_m ])
			string.append(alph[
						(inputBytes[1] << 4) & base32encoder.b32_m ]) 
			pad_count = 4
		# three bytes ~ 24 bits => set string[3] and string[4]
		if len(inputBytes) > 2:
			string[3] =(alph[
						((inputBytes[1] << 4) | inputBytes[2] >> 4) & base32encoder.b32_m ])
			string.append(alph[
						(inputBytes[2] << 1) & base32encoder.b32_m])	 
			pad_count = 3
		# string[4] string[5] and string[6]
		if len(inputBytes) > 3:
			string[4] =(alph[				
						((inputBytes[2] << 1) | inputBytes[3] >> 7) & base32encoder.b32_m ])
			string.append(alph[
						(inputBytes[3] >> 2) & base32encoder.b32_m ])
			string.append(alph[
						(inputBytes[3] << 3) & base32encoder.b32_m])
			pad_count = 1
		
		string.append(pad_char*pad_count)
		result = ''.join(string)
		
		return result
			
	def str2base32(self,data):#
		'''
encode data to base32

:param data: data to be encoded
:return: the `base32` encoding of data
:rtype: `str`
		'''
		base32_result = []
		
		
		# j is a counter 
		j = 0
		# first we process all 3-er groups if possible, using the
		# method ::self._process3byte::
		for i in range(0, len(data), 5):
			if (len(data)-j) < 5:
				break
			block_data = data[i:i+5]
			j = j+5
			
			base32_result.append(
				self._process5byte(block_data ))
		
		if (len(data) -j > 0):
			last_block = data[j:]
			base32_result.append( self._process_last_block(last_block) )
		
		result = ''.join(base32_result)
		return result
		pass
	
	def _process8byte(self,block):
		mask_1 = int("00000011",2)
		mask_3 = int("00001111",2)
		mask_4 = int("00000001",2)
		mask_6 = int("00000111",2)
		code = []
		alph = base32_alp
		for i in block:
			code.append(alph.index(i))
			
		string_result = ["","","","",""]
		string_result[0] = chr(
			code[0] << 3 | code[1] >> 2)
		string_result[1] = chr(
			( (code[1] & mask_1) << 6) | (code[2] << 1) | (code[3] >>4))
		string_result[2] = chr(
			( (code[3] & mask_3) << 4) | (code[4] >> 1))
		string_result[3] = chr(
			( (code[4] & mask_4) << 7) | (code[5] << 2) | (code[6] >>3))
		string_result[4] = chr(
			( (code[6] & mask_6) << 5) | code[7] )
		
		return ''.join(string_result)
		pass
	
	def _process_padded_block(self,block):
		mask_1 = int("00000011",2)
		mask_3 = int("00001111",2)
		mask_4 = int("00000001",2)
		code = []
		alph = base32_alp
		for i in block:
			code.append(alph.index(i))
		string_result = ["","","","",""]
		result = None
		
		if (block[2] == pad_char):
			string_result[0] = chr(
				code[0] << 3 | code[1] >> 2 )
			result = ''.join(string_result)
		elif(block[4] == pad_char):
			string_result[0] = chr(
				code[0] << 3 | code[1] >> 2)
			string_result[1] = chr(
				( (code[1] & mask_1) << 6) | (code[2] << 1) | (code[3] >>4)	)
			result = ''.join(string_result)
		elif(block[5] == pad_char):
			string_result[0] = chr(
				code[0] << 3 | code[1] >> 2)
			string_result[1] = chr(
				( (code[1] & mask_1) << 6) | (code[2] << 1) | (code[3] >>4))
			string_result[2] = chr(
				( (code[3] & mask_3) << 4) | (code[4] >> 1))
			result = ''.join(string_result)
		elif(block[6] == pad_char):
			string_result[0] = chr(
				code[0] << 3 | code[1] >> 2)
			string_result[1] = chr(
				( (code[1] & mask_1) << 6) | (code[2] << 1) | (code[3] >>4))
			string_result[2] = chr(
				( (code[3] & mask_3) << 4) | (code[4] >> 1))
			string_result[3] = chr(
				( (code[4] & mask_4) << 7) | (code[5] << 2) | (code[6] >>3))
			result = ''.join(string_result)
		else:
			result = self._process8byte(block)
			
		return result
	
	def base322str(self,data):
		i = 0
		data_result = []
		
		for i in range(0,len(data)-8,8):
			buff = data[i:i+8]
			data_result.append(self._process8byte(buff))
			
		data_result.append(
			self._process_padded_block(data[-8:]))
		return ''.join(data_result)
	
# ============================ how to use ====================== #
if __name__ == "__main__":
	import base64
	x = base32encoder()
	testString = "foobazx"
	z = base64.b32encode(testString)
	t = x.base322str(z)
	
	print "test = >>%s<< z = >>%s<< t = >>%s<<" %(testString,z,t)
	